#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "contexto.h"
#include "abb.h"
#include "contContextos.h"

//Definiciones
#define CaracterSeparador	'.'
struct palabra{
	char* valor;
	unsigned short frecuencia;
};
struct contenedorContextos{
	abb_t* contextos;
	unsigned short cantidad;
	unsigned int file_id;
};
struct contexto{
	char * valor;
	unsigned short apariciones;
	abb_t* palabras;
};

//Pre: Se le pasa el numero de documento que se esta parseando
//Post: devuelve un puntero al contenedor de contexto nuevo si se no hubo problemas o null en otro caso.
ctdctx_t* ctdctx_crear(unsigned int numDoc){
	
	ctdctx_t* nuevoContenedor=malloc(sizeof(ctdctx_t));
	if (!nuevoContenedor)
		return NULL;
	nuevoContenedor->cantidad =0;
	nuevoContenedor->contextos = abb_crear(strcmp,ctx_destruir);
	if (!nuevoContenedor->contextos){
		free(nuevoContenedor);
		return NULL;
	}
	nuevoContenedor->file_id = numDoc;
	return nuevoContenedor;
}

//Pre:se le pasa una palabra contexto y una palabra a ingresar en el contexto
//Post:  si el contexto no exite devuelve false, si el contexto existe y la palabra ingresada es correcta devuelve true
//devuelve false en cualquier otro caso.
bool ctdctx_ingresar_palabra(ctdctx_t* contenedor,char *unContexto,char* unaPalabra){
	if ( (!unContexto) || (!unaPalabra) || (!contenedor))
		return false;
	if ( ( strcmp(unContexto,"") ==0 ) || ( strcmp(unaPalabra,"") ==0 ) )
		return false;
		
	if(!abb_pertenece(contenedor->contextos,unContexto)){
		//es un contexto nuevo
		contexto_t* nuevoContexto = ctx_crear(unContexto);
		if (!nuevoContexto)
			return false;
		bool control = ctx_ingresar_palabra(nuevoContexto,unaPalabra);

		if (!control)
			return false;//si hubo error	
		control=abb_guardar(contenedor->contextos,unContexto,nuevoContexto);
		if (!control){
			ctx_destruir(nuevoContexto);
			return false;//si hubo error
		}
		contenedor->cantidad = contenedor->cantidad +2;
	}else{
		contexto_t* contextoViejo = abb_obtener(contenedor->contextos,unContexto);
		bool control = ctx_ingresar_palabra(contextoViejo,unaPalabra);
		if (!control)
			return false;//si hubo error
			contenedor->cantidad++;
	}
		
	return true;
}

//Pre:se le pasa un contenedor
//Post:  si el contenedor no exite o esta vacio devuelve 0, devuelve
//la cantidad de palabras en otro caso
unsigned short ctdctx_cantidad(ctdctx_t* unContenedor){
	if (!unContenedor)
		return 0;
	return unContenedor->cantidad;
}
//PRE: se le pasa un contenedor
//Post: persiste en un txt los terminos contenidos y su aparicion
void persistir_contenedor(ctdctx_t* unContenedor){
	if (!unContenedor)
		return;
	abb_iter_t* iterContextos= abb_iter_in_crear(unContenedor->contextos);
	if (!iterContextos)
		return;
	char file_name [100] = "";
	//se limita a 2ᶺ88 ducumentos posibles
	sprintf(file_name, "document%d.temp", unContenedor->file_id);
	FILE* fd_terminos = fopen(file_name,"w");
	
	while (!abb_iter_in_al_final(iterContextos)){
		contexto_t* unContexto = (contexto_t*)abb_iter_in_ver_actual(iterContextos);
		//guardo en txt
		fprintf(fd_terminos, "%s%c%d%c%d\n", unContexto->valor, 
			CaracterSeparador, unContexto->apariciones, CaracterSeparador,unContenedor->file_id);
		abb_iter_in_avanzar(iterContextos);
	}
	fclose(fd_terminos);
	abb_iter_in_destruir(iterContextos);
}

//Pre:se le pasa una palabra contexto y una palabra a ingresar en el contexto
//Post:  si el contexto no exite lo crea , si el contexto existe y la palabra ingresada es correcta devuelve true
//devuelve false en cualquier otro caso.
bool ctdctx_ingresar_palabra_en_cant(ctdctx_t* contenedor,char *unContexto,unsigned short cantidad){
	if ( (!unContexto) || (cantidad == 0) || (!contenedor))
		return false;
	if ( strcmp(unContexto,"") == 0)
		return false;
	if(!abb_pertenece(contenedor->contextos,unContexto)){
		//es un contexto nuevo
		contexto_t* nuevoContexto = ctx_crear(unContexto);
		//agrego la cantidad de apariciones deseadas
		nuevoContexto->apariciones = cantidad;
		bool control=abb_guardar(contenedor->contextos,unContexto,nuevoContexto);
		if (!control){
			ctx_destruir(nuevoContexto);
			return false;//si hubo error
		}
//		contenedor->cantidad = contenedor->cantidad +2;
		contenedor->cantidad = contenedor->cantidad;	
	}else{
		contexto_t* contextoViejo = abb_obtener(contenedor->contextos,unContexto);
		//agrego la cantidad de apariciones deseadas (ya sumo uno al ingresar)
		contextoViejo->apariciones = cantidad - 1;
		
	}
	return true;
}
//es de uso una INTERNO
//Pre:se le pasa un contexto y una cantidad de palabras com parametro
//Post: si se cumplen las precondiciones devuelve la frecuencia de aparicion
//de ese contexto luego de haber borrado esa cantidad, devuelve 0 en caso contrario
void ctx_borrar_apariciones(contexto_t* unContexto,unsigned short cantidad){
	if ( (!unContexto) || (!cantidad) )
		return ;
	unContexto->apariciones = unContexto->apariciones - cantidad;
	return;
	
}
//Pre:se le pasa un contenedor
//Post:  imprime en pantalla los terminos que tenia y su aparicion
void ctdctx_decodificar(ctdctx_t* unContenedor){
	if (!unContenedor)
		return;
	if (!unContenedor->contextos)
		return;
	abb_iter_t* iterContextos = abb_iter_in_crear(unContenedor->contextos);
	if (!iterContextos)
		return;
	
	//recorro los contextos en el contenedor
	while (!abb_iter_in_al_final(iterContextos)){
		contexto_t* unContexto = (contexto_t*)abb_iter_in_ver_actual(iterContextos);
		abb_iter_t* iterPalabras = abb_iter_in_crear(unContexto->palabras);
		if (iterPalabras){
			//recorro las palabras en el contexto
			while (!abb_iter_in_al_final(iterPalabras)){
				palabra_t* unaPalabra = (palabra_t*)abb_iter_in_ver_actual(iterPalabras);
				
				//controlo que la palabra no aparezca mas que el contexto o no sea contexto vacio
				bool control = true;
				if( (unContexto->apariciones == 0) ||(unaPalabra->frecuencia >unContexto->apariciones)){
					control = false;
				}
				
				//aca se puede variar la condicion
				if( (obtener_frecuencia(unaPalabra)>3) && (control) ){
					//contexto secundiario trae el segundo contexto del nuevo termino para restarle la frecuencia
					contexto_t* contextoSecundario = (contexto_t*)abb_obtener(unContenedor->contextos,unaPalabra->valor);
					//borro las apariciones del primer y segundo subtermino
					if ( ((unContexto->apariciones) - obtener_frecuencia(unaPalabra)) >= 0){
						ctx_borrar_apariciones(unContexto,obtener_frecuencia(unaPalabra));
					}else{
						//valido que no se pase porque como es sin insigno toma el valor maximo si es menor a cero
						ctx_borrar_apariciones(unContexto,unContexto->apariciones);
					}

					if ( ((contextoSecundario->apariciones) - obtener_frecuencia(unaPalabra)) >= 0){
						ctx_borrar_apariciones(contextoSecundario,obtener_frecuencia(unaPalabra));
					}else{
						//valido que no se pase porque como es sin insigno toma el valor maximo si es menor a cero
						ctx_borrar_apariciones(contextoSecundario,contextoSecundario->apariciones);
					}
					//armo y guardo el nuevo termino
					char* nuevotermino = malloc (strlen(unContexto->valor) + strlen(unaPalabra->valor) + 2);
					if (nuevotermino){
						strcpy(nuevotermino,unContexto->valor);
						strcat(nuevotermino," ");
						strcat(nuevotermino,unaPalabra->valor);
						ctdctx_ingresar_palabra_en_cant(unContenedor,nuevotermino,obtener_frecuencia(unaPalabra));
						free(nuevotermino);
					}
				}
				abb_iter_in_avanzar(iterPalabras);
			}
		}
		abb_iter_in_destruir(iterPalabras);
		abb_iter_in_avanzar(iterContextos);
	}
	abb_iter_in_destruir(iterContextos);	
	persistir_contenedor(unContenedor);
}



//Pre: se le pasa un contenedor de contextos por parametro
//Post: destruye el contexto
void ctdctx_destruir(ctdctx_t *unContenedor){
	if (!unContenedor)
		return;
	abb_destruir(unContenedor->contextos);
	free(unContenedor);
}
